package com.javalaoniu.tool.utils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * created by admin
 * date: 2018/9/5 19:32
 */
public class DateTimeUtil {
    private static final ConcurrentMap<String, DateTimeFormatter> FORMATTER_CACHE = new ConcurrentHashMap<>();
    private static final int PATTERN_CACHE_SIZE = 500;

    private static final DateTimeFormatter DATE_FORMATTER_DEFAULT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter DATETIME_FORMATTER_DEFAULT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 获取当前日期 yyyy-MM-dd
     *
     * @return
     */
    public static String today() {
        return format(LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()), Pattern.STYLE_1.val());
    }

    /**
     * 获取当前日期时间 yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    public static String now() {
        return format(LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()), Pattern.STYLE_4.val());
    }

    /**
     * 日期时间偏移计算
     * @param date 当前日期
     * @param field 偏移的字段（年Calendar.YEAR、月Calendar.MONTH、日Calendar.DATE、小时Calendar.HOUR_OF_DAY等等）
     * @param amount 偏移的量，正数为加，负数为减
     * @return 偏移后的日期
     */
    public static Date offset(Date date, int field, int amount){
        Calendar cl = Calendar.getInstance();
        cl.setTime(date);
        cl.add(field, amount);
        return cl.getTime();
    }

    /**
     * 计算两个日期的时间差
     * @param date1 当前日期
     * @param date2 当前日期
     * @param unit 单位，如ChronoUnit.DAYS就是计算两个日期之间的天数
     * @return 时间差
     */
    public static long between(Date date1, Date date2, ChronoUnit unit){
        LocalDate d1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate d2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return unit.between(d1, d2);
    }

    /**
     * 格式化时间 yyyy-MM-dd HH:mm:ss
     *
     * @param date date
     * @return
     */
    public static String format(Date date) {
        return format(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), Pattern.STYLE_4.val());
    }

    /**
     * 格式化时间
     *
     * @param date    date
     * @param pattern 格式
     * @return
     */
    public static String format(Date date, String pattern) {
        return format(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), pattern);
    }

    /**
     * 格式化时间 yyyy-MM-dd HH:mm:ss
     *
     * @param localDateTime localDateTime
     * @return
     */
    public static String format(LocalDateTime localDateTime) {
        DateTimeFormatter formatter = createCacheFormatter(Pattern.STYLE_4.val());
        return localDateTime.format(formatter);
    }

    /**
     * 格式化时间
     *
     * @param localDateTime localDateTime
     * @param pattern       格式
     * @return
     */
    public static String format(LocalDateTime localDateTime, String pattern) {
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        return localDateTime.format(formatter);
    }

    /**
     * 格式化字符串转为Date yyyy-MM-dd HH:mm:ss
     *
     * @param time 格式化时间
     * @return
     */
    public static Date parseDate(String time) {
        return Date.from(parseLocalDateTime(time, Pattern.STYLE_4.val()).atZone(ZoneId.systemDefault()).toInstant());

    }

    /**
     * 格式化字符串转为Date
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return
     */
    public static Date parseDate(String time, String pattern) {
        return Date.from(parseLocalDateTime(time, pattern).atZone(ZoneId.systemDefault()).toInstant());

    }

    /**
     * 格式化字符串转为LocalDateTime yyyy-MM-dd HH:mm:ss
     *
     * @param time 格式化时间
     * @return
     */
    public static LocalDateTime parseLocalDateTime(String time) {
        DateTimeFormatter formatter = createCacheFormatter(Pattern.STYLE_4.val());
        return LocalDateTime.parse(time, formatter);
    }

    /**
     * 格式化字符串转为LocalDateTime
     *
     * @param time    格式化时间
     * @param pattern 格式
     * @return
     */
    public static LocalDateTime parseLocalDateTime(String time, String pattern) {
        DateTimeFormatter formatter = createCacheFormatter(pattern);
        return LocalDateTime.parse(time, formatter);
    }

    /**
     * 返回给定日期的所在季度的最后一天
     *
     * @param date 某个日期
     * @return 某季度的最后一天
     */
    public static LocalDate lastDayOfQuarter(LocalDate date) {
        return LocalDate.of(date.getYear(), date.getMonth().firstMonthOfQuarter().plus(3), 1).with(TemporalAdjusters.lastDayOfMonth());
    }


    /**
     * 返回给定日期的所在季度的第一天
     *
     * @param date 某个日期
     * @return 某季度的第一天
     */
    public static LocalDate firstDayOfQuarter(LocalDate date) {
        return LocalDate.of(date.getYear(), date.getMonth().firstMonthOfQuarter(), 1);
    }

    /**
     * 返回给定日期的所在月的最后一天
     *
     * @param date 某个日期
     * @return 月的最后i一天
     */
    public static LocalDate lastDayOfMonth(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 返回给定日期的所在月的第一天
     *
     * @param date 某个日期
     * @return 月的第一天
     */
    public static LocalDate firstDayOfMonth(LocalDate date) {
        return LocalDate.of(date.getYear(), date.getMonth(), 1);
    }


    /**
     * 比较两个给定格式的字符串日期是否相等
     *
     * @param sourceDate 需要比较的日期
     * @param targetDate 比较的日期
     * @return
     */
    public static boolean equals(String sourceDate, String targetDate, String pattern) {
        DateTimeFormatter _pattern = DateTimeFormatter.ofPattern(pattern);
        LocalDate date1 = LocalDate.parse(sourceDate, _pattern);
        LocalDate date2 = LocalDate.parse(targetDate, _pattern);

        return equals(date1, date2);
    }

    /**
     * 比较两个字符串日期是否相等
     *
     * @param sourceDate 需要比较的日期
     * @param targetDate 比较的日期
     * @return
     */
    public static boolean equals(String sourceDate, String targetDate) {
        LocalDate date1 = LocalDate.parse(sourceDate, DATE_FORMATTER_DEFAULT);
        LocalDate date2 = LocalDate.parse(targetDate, DATE_FORMATTER_DEFAULT);

        return equals(date1, date2);
    }

    /**
     * 比较两个日期是否相等
     *
     * @param sourceDate 需要比较的日期
     * @param targetDate 比较的日期
     * @return
     */
    public static boolean equals(LocalDate sourceDate, LocalDate targetDate) {
        if (sourceDate.equals(targetDate)) {
            return true;
        }
        return false;
    }

    /**
     * 字符串日期转为LocalDate日期
     *
     * @param date 需要转换的字符串日期
     * @return
     */
    public static LocalDate parse(String date) {
        return LocalDate.parse(date, DATE_FORMATTER_DEFAULT);
    }

    /**
     * 根据字符串格式把字符串日期转为LocalDate日期
     *
     * @param date 需要转换的字符串日期
     * @param pattern 字符串日期的格式
     * @return
     */
    public static LocalDate parse(String date, String pattern) {
        DateTimeFormatter _pattern = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.parse(date, _pattern);
    }

    /**
     * 在缓存中创建DateTimeFormatter
     *
     * @param pattern 格式
     * @return
     */
    private static DateTimeFormatter createCacheFormatter(String pattern) {
        if (pattern == null || pattern.length() == 0) {
            throw new IllegalArgumentException("Invalid pattern specification");
        }
        DateTimeFormatter formatter = FORMATTER_CACHE.get(pattern);
        if (formatter == null) {
            if (FORMATTER_CACHE.size() < PATTERN_CACHE_SIZE) {
                formatter = DateTimeFormatter.ofPattern(pattern);
                DateTimeFormatter oldFormatter = FORMATTER_CACHE.putIfAbsent(pattern, formatter);
                if (oldFormatter != null) {
                    formatter = oldFormatter;
                }
            }
        }

        return formatter;
    }

    public enum Pattern {

        /**
         * 时间格式：yyyy-MM-dd
         */
        STYLE_1("yyyy-MM-dd"),

        /**
         * 时间格式：yyyy/MM/dd
         */
        STYLE_2("yyyy/MM/dd"),

        /**
         * 时间格式：yyyyMMdd
         */
        STYLE_3("yyyyMMdd"),

        /**
         * 长时间格式：yyyy-MM-dd HH:mm:ss
         */
        STYLE_4("yyyy-MM-dd HH:mm:ss"),

        /**
         * 长时间格式：yyyy/MM/dd HH:mm:ss
         */
        STYLE_5("yyyy/MM/dd HH:mm:ss"),

        /**
         * 长时间格式：yyyyMMddHHmmss
         */
        STYLE_6("yyyyMMddHHmmss"),

        /**
         * 长时间格式 年月日时分秒 带毫秒：yyyy-MM-dd HH:mm:ss.SSS
         */
        STYLE_7("yyyy-MM-dd HH:mm:ss.SSS"),

        /**
         * 长时间格式 年月日时分秒 带毫秒：yyyy/MM/dd HH:mm:ss.SSS
         */
        STYLE_8("yyyy/MM/dd HH:mm:ss.SSS"),

        /**
         * 长时间格式 年月日时分秒 带微秒：yyyy-MM-ddTHH:mm:ss.SSSSSS
         */
        STYLE_9("yyyy-MM-dd'T'HH:mm:ss.SSSSSS");

        private String pattern;

        Pattern(String pattern) {
            this.pattern = pattern;
        }

        public String val() {
            return pattern;
        }
    }

    public static void main(String[] args) {
        System.out.println(LocalDateTime.now());
        System.out.println(DateTimeUtil.today());
        System.out.println(DateTimeUtil.now());
        System.out.println(DateTimeUtil.format(new Date(), Pattern.STYLE_5.val()));
        System.out.println(DateTimeUtil.parseDate("2018-05-12 12:45:33"));
        System.out.println(DateTimeUtil.offset(new Date(), Calendar.MONTH, 2));
    }
}
